import { ECSErr } from "../err/ECSErr";
import { RecyclePool } from "../util/Pool";
import { ECSEntity } from "./ECSEntity";
import { ECSMgr } from "./ECSMgr";

/**
 * [ECS]实体管理
 */
export class ECSEntityMgr extends ECSMgr {
	/** 对象池 */
	protected _pool = new RecyclePool(ECSEntity);
	/** 当前实体最大id */
	protected _eid: number = 0;
	/** 场景里所有的实体 */
	protected _entityMap = new Map<number, ECSEntity>();
	/** 等待被移除的实体 */
	protected _waitRemoveEntitySet = new Set<ECSEntity>();

	/**
	 * 获取一个新的实体id
	 */
	getId() {
		return ++this._eid;
	}

	/**
	 * 创建一个实体添加到场景中
	 */
	create() {
		const entity = this._pool.alloc();
		entity.id = this.getId();
		entity.scene = this.scene;
		this._entityMap.set(entity.id, entity);
		this.scene.filterMgr.onEntityUpdate(entity);

		return entity;
	}

	/**
	 * 通过实体id获取场景中的实体
	 * @param id 实体id
	 */
	get(id: number) {
		return this._entityMap.get(id);
	}

	/**
	 * 遍历场景里的实体
	 * @param each
	 */
	each(each: (entity: ECSEntity) => any) {
		return this._entityMap.forEach(each);
	}

	/**
	 * 遍历寻找场景里的实体
	 * @param each 返回true时则返回对应实体
	 */
	find(each: (entity: ECSEntity) => any) {
		const keys = this._entityMap.keys();
		for (const key of keys) {
			const entity = this._entityMap.get(key);
			if (each(entity)) {
				return entity;
			}
		}
		return null;
	}

	/**
	 * 移除实体 在帧结束时才会真正从场景中移除
	 * @param entity 实体
	 */
	remove(entity: ECSEntity) {
		if (!entity) {
			throw new ECSErr("移除不存在的实体");
		}

		entity.removed = true;
		if (this.scene.inFrame) {
			this._waitRemoveEntitySet.add(entity);
		} else {
			this.removeEntity(entity);
			this.freeEntity(entity);
		}
	}

	/**
	 * 更新实体
	 */
	update() {
		if (this._waitRemoveEntitySet.size > 0) {
			//所有等待移除的实体在这里统一移除
			this._waitRemoveEntitySet.forEach(this.removeEntity, this);
			this._waitRemoveEntitySet.forEach(this.freeEntity, this);
			this._waitRemoveEntitySet.clear();
		}
	}

	/**
	 * 更新实体的onStart逻辑
	 */
	updateStart() {
		this._entityMap.forEach(this.startEntity, this);
	}

	private startEntity(entity: ECSEntity) {
		entity.start();
	}

	private removeEntity(entity: ECSEntity) {
		entity.onRemove();
		this._entityMap.delete(entity.id);
		this.scene.filterMgr.onEntityUpdate(entity);
	}

	private freeEntity(entity: ECSEntity) {
		this._pool.free(entity);
	}

	protected onDestroy(): void {
		this._entityMap.forEach((e: ECSEntity) => {
			e.cmpMap.forEach(cmp => cmp.onFree());
			e.cmpMap.clear();
			this.freeEntity(e);
		});
		this._entityMap.clear();
		this._waitRemoveEntitySet.clear();
		this._pool.clear();
	}
}
